/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Manager.h"
#include <filesystem/Finder.h>

#ifdef ANDROID_NDK
#include "MultiFileAndroid.h"
#elif IPHONE_VERSION
#include "MultiFile_iPhone.h"
#else
#include "MultiFile.h"
#endif

#include "File.h"
#include <Gluck.h>

#ifdef IPHONE_VERSION
#include <CoreFoundation/CFLocale.h>
#include <CoreFoundation/CFString.h>

#include <sys/sysctl.h>  
#include <mach/mach.h>
#endif

#include <system/Environment.h>
#ifdef NETFILESYSTEM_URL
#include "NetFile.h"
#endif

#ifdef IPHONE_VERSION
extern bool isPad();
#endif

#include "Config.h"

namespace liba
{
namespace resource
{

static MultiFile * multi_file = 0;
static std::string locale_string;

#ifdef WIN_VERSION
class RarFinder :
	public filesystem::Finder
{
protected:
	virtual bool on_file( const filesystem::String & path )
	{
		multi_file->add_multifile( path );
		return true;
	}
};
#endif

Manager::Creator::Creator()
{
	locale_string = Environment::instance()->getLanguage();

#ifdef WIN_VERSION
	if( multi_file )
		THROW(Gluck(__FUNCTION__, "multi_file already exist"));
	multi_file = new MultiFile("");
	RarFinder finder;
	filesystem::find( &finder, "%app_path%", "*.rar" );
#endif

#ifdef IPHONE_VERSION
	CFLocaleRef locale = CFLocaleCopyCurrent();
	CFStringRef locale_id = CFLocaleGetIdentifier(locale);
	
	char buffer[1024];
	CFStringGetCString( locale_id, buffer, 1024, CFStringGetSystemEncoding() );
	locale_string = buffer;
	printf("current locale: %s\n", locale_string.c_str());
#endif
}

#ifdef IPHONE_VERSION
static double availableMemory()
{
	vm_statistics_data_t vmStats;
	mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;
	kern_return_t kernReturn = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount);
	
	if(kernReturn != KERN_SUCCESS) {
		return -1;
	}
		
	return ((vm_page_size * vmStats.free_count) / 1024.0) / 1024.0;
}
#endif
	
Manager::Creator::Creator(const std::string & param)
{
	locale_string = Environment::instance()->getLanguage();

	if( multi_file )
		THROW(Gluck(__FUNCTION__, "multi_file already exist"));
	multi_file = new MultiFile(param);

#ifdef WIN_VERSION
	RarFinder finder;
	filesystem::find( &finder, "%app_path%", "*.rar" );
#endif
	
#ifdef IPHONE_VERSION
	CFLocaleRef locale = CFLocaleCopyCurrent();
	CFStringRef locale_id = CFLocaleGetIdentifier(locale);
	
	char buffer[1024];
	CFStringGetCString( locale_id, buffer, 1024, CFStringGetSystemEncoding() );
	locale_string = buffer;
	printf("current locale: %s\n", locale_string.c_str());	
	
	CFArrayRef languages_arr = CFLocaleCopyPreferredLanguages();
	int count = CFArrayGetCount(languages_arr);
	if(count > 0)
	{
		CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(languages_arr, 0);
		char buffer[1024];
		CFStringGetCString( str, buffer, 1024, CFStringGetSystemEncoding() );
		locale_string = buffer;
		printf("preffered language: %s\n", locale_string.c_str());			
	}
	
	printf("available memory: %f\n", availableMemory());			
#endif
}

Manager::Creator::~Creator()
{
	delete multi_file; multi_file = 0;
}
	
#ifdef IPHONE_VERSION
Resource * Manager::create_resource_from_documents(const filesystem::String & name)
{
	filesystem::String multilanguage_name = name;
	
	std::string str_to_lower;
	const char * buff = name.c_str();
	for( unsigned i = 0; i < name.size(); ++i )
	{
		str_to_lower += tolower(buff[i]);
	}
	
	Resource * res = multi_file->create_resource( str_to_lower.c_str() );
	
	if( res )
		return res;
	
	if(availableMemory() > 50)
	{
		//we are >= 3GS or iPad, try hd resource
		filesystem::String multilanguage_name = filesystem::String("%local_appdata%") + "/hd/" + str_to_lower.c_str();
		
		//try localized resource hd
		filesystem::String localized_str = filesystem::String("%local_appdata%") + "/hd/" + locale_string + "/" + str_to_lower.c_str();
		File * frw = new File( localized_str );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
		
		frw = new File( multilanguage_name );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
	}

	if(isPad())
	{
		filesystem::String localized_str = filesystem::String("%local_appdata%") + "/ipad/" + locale_string.c_str() + "/" + str_to_lower.c_str();
		File * frw = new File( localized_str );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
		
		multilanguage_name = filesystem::String("%local_appdata%") + "/ipad/" + str_to_lower.c_str();
		frw = new File( multilanguage_name );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
		
	}
	
	filesystem::String localized_str = filesystem::String("%local_appdata%") + "/" + locale_string.c_str() + "/" + str_to_lower.c_str();
	
	File * frw = new File( localized_str );
	
	if( frw->is_valid() )
		return frw;
	delete frw; frw = 0;
	
	//default name in bundle
	multilanguage_name = filesystem::String("%local_appdata%") + "/" + str_to_lower.c_str();
	frw = new File( multilanguage_name );
	if( frw->is_valid() )
		return frw;

	delete frw; frw = 0;
	
	//otherwise try to create directly
	frw = new File( name );
	if( frw->is_valid() )
		return frw;
	delete frw; frw = 0;
	
	return 0;
}	
#endif

Resource * Manager::create_resource(const filesystem::String & name)
{
#if defined(ALLOW_USER_RESOURCE_OVERRIDES) && !defined(MARMALADE)
	{
		filesystem::String userResourceName(name + USER_RESOURCE_SUFFIX);
		Resource* userResource = Manager::create_resource_impl(userResourceName);

		if (userResource)
			return userResource;
	}
#endif
	return Manager::create_resource_impl(name);
}

Resource * Manager::create_resource_impl(const filesystem::String & name)
{
#ifdef NETFILESYSTEM_URL
	filesystem::String multilanguage_name_net = name;
#endif
	filesystem::String multilanguage_name = name;
	
	std::string str_to_lower;
	const char * buff = name.c_str();
	for( unsigned i = 0; i < name.size(); ++i )
	{
		str_to_lower += tolower(buff[i]);
	}

	Resource * res = 0;
    if (multi_file)
        multi_file->create_resource( str_to_lower.c_str() );

	if( res )
		return res;

#ifdef IPHONE_VERSION
	if(availableMemory() > 50)
	{
		//we are >= 3GS or iPad, try hd resource
#ifdef NETFILESYSTEM_URL
		filesystem::String multilanguage_name_net = "/hd/" + str_to_lower.c_str();
#endif
		filesystem::String multilanguage_name = filesystem::String("%app_path%") + "/hd/" + str_to_lower.c_str();
		
		//try localized resource hd
#ifdef NETFILESYSTEM_URL
		filesystem::String localized_str_net = "/hd/" + locale_string + "/" + str_to_lower.c_str();
#endif
		filesystem::String localized_str = filesystem::String("%app_path%") + "/hd/" + locale_string.c_str() + "/" + str_to_lower.c_str();
#ifdef NETFILESYSTEM_URL
		// try network filesystem first
		NetFile * frwn = new NetFile( localized_str_net );
		if( frwn->is_valid() )
			return frwn;
		delete frwn; frwn = 0;
#endif
		File * frw = new File( localized_str );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
		
#ifdef NETFILESYSTEM_URL
		// try network filesystem first
		frwn = new NetFile( multilanguage_name_net );
		if( frwn->is_valid() )
			return frwn;
		delete frwn; frwn = 0;
#endif
		frw = new File( multilanguage_name );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
	}
#endif

#ifdef IPHONE_VERSION
	if(isPad())
	{
#ifdef NETFILESYSTEM_URL
		filesystem::String localized_str_net = "/ipad/" + locale_string.c_str() + "/" + str_to_lower.c_str();
#endif
		filesystem::String localized_str = filesystem::String("%app_path%") + "/ipad/" + locale_string.c_str() + "/" + str_to_lower.c_str();
		
#ifdef NETFILESYSTEM_URL
		// try network filesystem first
		NetFile * frwn = new NetFile( localized_str_net );
		if( frwn->is_valid() )
			return frwn;
		delete frwn; frwn = 0;
#endif
		File * frw = new File( localized_str );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
		
#ifdef NETFILESYSTEM_URL
		multilanguage_name_net = "/ipad/" + str_to_lower.c_str();
#endif
		multilanguage_name = filesystem::String("%app_path%") + "/ipad/" + str_to_lower.c_str();
		
#ifdef NETFILESYSTEM_URL
		// try network filesystem first
		frwn = new NetFile( multilanguage_name_net );
		if( frwn->is_valid() )
			return frwn;
		delete frwn; frwn = 0;
#endif
		frw = new File( multilanguage_name );
		if( frw->is_valid() )
			return frw;
		delete frw; frw = 0;
		
	}
#endif

#ifdef NETFILESYSTEM_URL
	filesystem::String localized_str_net = "/" + locale_string.c_str() + "/" + str_to_lower.c_str();
#endif
	filesystem::String localized_str = filesystem::String("%app_path%") + "/" + locale_string.c_str() + "/" + str_to_lower.c_str();

#ifdef NETFILESYSTEM_URL
	// try network filesystem first
	NetFile * frwn = new NetFile( localized_str_net );
	if( frwn->is_valid() )
		return frwn;
	delete frwn; frwn = 0;
#endif
	
	File * frw = new File( localized_str );
	if( frw->is_valid() )
		return frw;
	delete frw; frw = 0;

	//default name in bundle
#ifdef NETFILESYSTEM_URL
	multilanguage_name_net = "/" + str_to_lower.c_str();
#endif
	multilanguage_name = filesystem::String("%app_path%") + "/" + str_to_lower.c_str();
	
#ifdef NETFILESYSTEM_URL
	// try network filesystem first
	frwn = new NetFile( multilanguage_name_net );
	if( frwn->is_valid() )
		return frwn;
	delete frwn; frwn = 0;
#endif
	
	frw = new File( multilanguage_name );
	if( frw->is_valid() )
		return frw;
	delete frw; frw = 0;
	
	//otherwise try to create directly
#ifdef NETFILESYSTEM_URL
	// try network filesystem first
	frwn = new NetFile( name );
	if( frwn->is_valid() )
		return frwn;
	delete frwn; frwn = 0;
#endif
	frw = new File( name );
	if( frw->is_valid() )
		return frw;
	delete frw; frw = 0;
	
	return 0;
}

bool Manager::find(filesystem::Finder * finder, const filesystem::String & base_path)
{
#ifdef WIN_VERSION
	if( !multi_file->find( finder, base_path ) )
		return false; // asked to stop search
#endif
		
	return filesystem::find( finder, base_path );
}

} // namespace resource
} // namespace liba

